Go 原子计数器

Go里面的管理协程状态的主要机制就是通道通讯。这些我们上面的例子介绍过。这里还有一些管理状态的机制,下面我们看看多协程原子访问计数器的例子,这个功能是由sync/atomic包提供的函数来实现的。

  1. package main
  2. import "fmt"
  3. import "time"
  4. import "sync/atomic"
  5. import "runtime"
  6. func main() {
  7. // 我们使用一个无符号整型来代表一个永远为正整数的counter
  8. var ops uint64 = 0
  9. // 为了模拟并行更新,我们使用50个协程来每隔1毫秒来
  10. // 增加一下counter值,注意这里的50协程里面的for循环,
  11. // 也就是说如果主协程不退出,这些协程将永远运行下去
  12. // 所以这个程序每次输出的值有可能不一样
  13. for i := 0; i < 50; i++ {
  14. go func() {
  15. for {
  16. // 为了能够保证counter值增加的原子性,我们使用
  17. // atomic包中的AddUint64方法,将counter的地址和
  18. // 需要增加的值传递给函数即可
  19. atomic.AddUint64(&ops, 1)
  20. // 允许其他的协程来处理
  21. runtime.Gosched()
  22. }
  23. }()
  24. }
  25. //等待1秒中,让协程有时间运行一段时间
  26. time.Sleep(time.Second)
  27. // 为了能够在counter仍被其他协程更新值的同时安全访问counter值,
  28. // 我们获取一个当前counter值的拷贝,这里就是opsFinal,需要把
  29. // ops的地址传递给函数`LoadUint64`
  30. opsFinal := atomic.LoadUint64(&ops)
  31. fmt.Println("ops:", opsFinal)
  32. }

我们多运行几次,结果如下:

  1. ops: 7499289
  2. ops: 7700843
  3. ops: 7342417